import gc
import os
import re
import md5
import sys
import time
import resource
import threading
import catalogs;
from collections import deque;
import Queue;
import pygtk
import gtk
import gtk.gdk;
import gobject
import events;

class CacheElement:
    file = None;
    pixbuf = None;
    next = None;
    prev = None;
    
class Thumbnailer(events.EventSource):
    
    queue = Queue.Queue();
    queuedFiles = set();
    
    def __init__(self,useThread = False):
        events.EventSource.__init__(self);
        self.useThread = useThread;
        if useThread:
            self.worker = threading.Thread(target=self.threadWork);
            self.worker.setDaemon(True);
            self.worker.start();
        else:
            self.idle = False;
        
    def getCatalog(self):
        return self.__catalog;
        
    def setCatalog(self,catalog):
        self.__catalog = catalog;
        if catalog == None:
            self.queue.join();
        if self.__catalog != None:
            self.maxWidth = self.__catalog.getIntOption(catalogs.OPTION_MAX_WIDTH);
            self.maxHeight = self.__catalog.getIntOption(catalogs.OPTION_MAX_HEIGHT);
    
    catalog = property(getCatalog,setCatalog);
    
    def threadWork(self):
        while True:
            self.work(True);
            
    def work(self,wait):
        try:
            (file,maxWidth,maxHeight) = self.queue.get(wait);
            try:
                self.queuedFiles.remove(file);
                catalog = self.__catalog;
                if catalog != None:
                    pixbuf = self.catalog.getThumbnail(file);
                    if pixbuf == None:
                        pixbuf = gtk.gdk.pixbuf_new_from_file(self.__catalog.resolveFile(file));
                        pixbuf = self.resizeTo(pixbuf, self.maxWidth, self.maxHeight);
                        catalog.setThumbnail(file,pixbuf);
                    pixbuf = self.resizeTo(pixbuf, maxWidth, maxHeight);
                    self.emit('loaded',file,pixbuf);
                    return True;
            finally:
                self.queue.task_done();
        except Queue.Empty:
            self.idle = False;
            return False;
    
    def idleWork(self):
        return self.work(False);
    
    def loadThumbnail(self,file,maxWidth,maxHeight):
        if self.catalog == None:
            return None;
        pixbuf = self.catalog.getThumbnail(file);
        if pixbuf == None:
            if file not in self.queuedFiles:
                self.queuedFiles.add(file);
                self.queue.put((file,maxWidth,maxHeight),True,None);
                if not self.useThread and not self.idle:
                    self.idle = True;
                    gobject.idle_add(self.idleWork);
        else:
            return self.resizeTo(pixbuf,maxWidth,maxHeight);
        
    def resizeTo(self,pixbuf,maxWidth,maxHeight):
        width = pixbuf.get_width();
        height = pixbuf.get_height();
        if (width == maxWidth and height <= maxHeight) or \
           (height == maxHeight and width <= maxWidth):
            return pixbuf;
        newWidth = self.maxWidth;
        if maxWidth > width:
            newHeight = maxWidth*height/width;
        else:
            newHeight = height/(width/maxWidth);
        if newHeight > maxHeight:
            if maxHeight > height:
                newWidth = maxHeight*width/height;
            else:
                newWidth = width/(height/maxHeight);
            newHeight = maxHeight;
        return pixbuf.scale_simple(newWidth, newHeight, gtk.gdk.INTERP_BILINEAR);
        
class ThumbnailCache(events.EventSource):
    cacheSize = 10;
    cache = {};
    cacheLock = threading.RLock();
    callbacks = [];
    thumbnailer = None;
    maxWidth = 128;
    maxHeight = 96;
    
    def __init__(self,thumbnailer,maxWidth,maxHeight,cacheSize):
        events.EventSource.__init__(self);
        self.thumbnailer = thumbnailer;
        self.maxWidth = maxWidth;
        self.maxHeight = maxHeight;
        self.cacheSize = cacheSize;
        self.thumbnailer.connect("loaded",self.insertInCache);
        self.first = None;
        self.last = None;
    
    def getThumbnail(self,file):
        self.cacheLock.acquire();
        try:
            if self.cache.has_key(file):
                element = self.cache[file];
                if element.prev != None:
                    prev = element.prev;
                    next = element.next;
                    element.next = self.first.next;
                    element.prev = None;
                    if prev != None:
                        prev.next = next;
                    if next != None:
                        next.prev = prev;
                    else:
                        self.last = prev;
                    self.first.prev = element;
                    self.first = element;
                self.emit("loaded",file, element.pixbuf);
                return element.pixbuf;
            else:
                pixbuf = self.thumbnailer.loadThumbnail(file,self.maxWidth,self.maxHeight);
                if pixbuf != None:
                    self.insertInCache(self.thumbnailer, file, pixbuf);
                return pixbuf;
        finally:
            self.cacheLock.release();
            
    def insertInCache(self,thumbnailer,file,pixbuf):
        self.cacheLock.acquire();
        try:
            element = self.cache[file] = CacheElement();
            element.pixbuf = pixbuf;
            element.file = file;
            element.next = self.first;
            element.prev = None;
            if self.first == None:
                self.first = self.last = element;
            else:
                self.first.prev = element;
                self.first = element;
            self.emit("loaded",file, pixbuf);
            if len(self.cache) > self.cacheSize:
                self.emit("removed",self.last.file);
                del self.cache[self.last.file];
                self.last = self.last.prev;
                self.last.next = None;
        finally:
            self.cacheLock.release();
